package com.example.func.service.impl;

import com.example.func.config.GitConfig;
import com.example.func.dto.request.CreateBranchWithRepoRequest;
import com.example.func.dto.request.MergeBranchRequest;
import com.example.func.dto.request.RollbackRequest;
import com.example.func.dto.response.BranchInfo;
import com.example.func.dto.response.MergeResult;
import com.example.func.service.GitService;
import com.example.func.utils.MavenDependencyCounter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Git 操作服务实现
 */
@Slf4j
@Service
public class GitServiceImpl implements GitService {

    /**
     * 从远程URL中提取仓库名称
     */
    private String extractRepoNameFromUrl(String remoteUrl) {
        try {
            if (remoteUrl == null || remoteUrl.trim().isEmpty()) {
                return "default";
            }
            
            // 处理各种URL格式
            // https://gitee.com/chen1007/demotest.git -> demotest
            // https://github.com/user/repo.git -> repo
            // git@github.com:user/repo.git -> repo
            
            String url = remoteUrl.trim();
            
            // 移除 .git 后缀
            if (url.endsWith(".git")) {
                url = url.substring(0, url.length() - 4);
            }
            
            // 提取最后一个路径段作为仓库名
            String[] parts = url.split("/");
            if (parts.length > 0) {
                String repoName = parts[parts.length - 1];
                log.info("从URL {} 提取仓库名称: {}", remoteUrl, repoName);
                return repoName;
            }
            
            return "default";
        } catch (Exception e) {
            log.error("提取仓库名称失败: {}", e.getMessage());
            return "default";
        }
    }

    /**
    /**
     * 获取Git仓库路径，如果不存在则创建
     */
    private String getRepositoryPath() {
        // 从远程URL中动态提取仓库名称
        String repoName = extractRepoNameFromUrl(gitConfig.getRemoteUrl());
        String userDir = System.getProperty("user.dir");
        String repoPath = userDir + File.separator + "repos" + File.separator + repoName;
        
        log.info("Git仓库路径: {}", repoPath);
        
        File repoDir = new File(repoPath);
        if (!repoDir.exists()) {
            log.info("仓库目录不存在，创建目录: {}", repoPath);
            repoDir.mkdirs();
        }
        
        // 检查是否是Git仓库，如果不是则初始化
        File gitDir = new File(repoDir, ".git");
        if (!gitDir.exists()) {
            log.info("初始化Git仓库: {}", repoPath);
            try {
                Git.init().setDirectory(repoDir).call().close();
                log.info("Git仓库初始化成功: {}", repoPath);
            } catch (Exception e) {
                log.error("初始化Git仓库失败: {}", e.getMessage());
                throw new RuntimeException("初始化Git仓库失败: " + e.getMessage());
            }
        }
        
        return repoPath;
    }

    @Autowired
    private GitConfig gitConfig;

    /**
     * 查找分支引用（优先本地分支，其次远程分支）
     */
    private Ref findBranchRef(Git git, String branchName) {
        try {
            // 1. 先尝试查找本地分支
            Ref localRef = git.getRepository().findRef("refs/heads/" + branchName);
            if (localRef != null) {
                log.info("找到本地分支: {}", branchName);
                return localRef;
            }
            
            // 2. 再尝试查找远程分支
            Ref remoteRef = git.getRepository().findRef("refs/remotes/origin/" + branchName);
            if (remoteRef != null) {
                log.info("找到远程分支: origin/{}", branchName);
                return remoteRef;
            }
            
            // 3. 如果都没找到，记录所有可用的分支
            log.warn("分支 {} 不存在，可用的分支:", branchName);
            List<Ref> localBranches = git.branchList().call();
            for (Ref ref : localBranches) {
                log.warn("  本地分支: {}", ref.getName());
            }
            
            List<Ref> remoteBranches = git.branchList()
                    .setListMode(org.eclipse.jgit.api.ListBranchCommand.ListMode.REMOTE)
                    .call();
            for (Ref ref : remoteBranches) {
                log.warn("  远程分支: {}", ref.getName());
            }
            
            return null;
        } catch (Exception e) {
            log.error("查找分支引用失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 确保本地分支存在，如果不存在则从远程分支创建
     */
    private void ensureLocalBranchExists(Git git, String branchName) {
        try {
            // 检查本地分支是否存在
            Ref localRef = git.getRepository().findRef("refs/heads/" + branchName);
            if (localRef != null) {
                log.info("本地分支 {} 已存在", branchName);
                return;
            }
            
            // 检查远程分支是否存在
            Ref remoteRef = git.getRepository().findRef("refs/remotes/origin/" + branchName);
            if (remoteRef != null) {
                log.info("从远程分支创建本地分支: {} -> {}", "origin/" + branchName, branchName);
                git.branchCreate()
                        .setName(branchName)
                        .setStartPoint("origin/" + branchName)
                        .call();
                log.info("本地分支 {} 创建成功", branchName);
            } else {
                log.warn("远程分支 origin/{} 不存在，无法创建本地分支", branchName);
            }
        } catch (Exception e) {
            log.error("确保本地分支存在失败: {}", e.getMessage());
            throw new RuntimeException("确保本地分支存在失败: " + e.getMessage());
        }
    }

    /**
     * 确保远程地址配置正确，如果配置不同则强制更新，并统一执行 fetch 获取最新分支信息
     */
    private void ensureRemoteUrl(Git git) {
        try {
            UsernamePasswordCredentialsProvider credentials = 
                new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
            
            // 检查当前远程地址
            String currentUrl = git.getRepository().getConfig().getString("remote", "origin", "url");
            String targetUrl = gitConfig.getRemoteUrl();
            log.info("当前远程地址: {} 目标远程地址:{}", currentUrl,targetUrl);
            
            if (currentUrl == null || !currentUrl.equals(targetUrl)) {
                log.info("远程地址需要更新: {} -> {}", currentUrl, targetUrl);
                
                // 先尝试删除现有远程地址
                try {
                    git.remoteRemove().setRemoteName("origin").call();
                    log.info("删除旧远程地址: {}", currentUrl);
                } catch (Exception e) {
                    log.debug("删除远程地址失败（可能不存在）: {}", e.getMessage());
                }
                
                // 添加新的远程地址
                git.remoteAdd()
                        .setName("origin")
                        .setUri(new URIish(targetUrl))
                        .call();
                log.info("添加新远程地址: {}", targetUrl);
                
                // 清理旧的远程分支引用
                try {
                    // 删除所有旧的远程分支引用
                    List<Ref> oldRemoteBranches = git.branchList()
                            .setListMode(org.eclipse.jgit.api.ListBranchCommand.ListMode.REMOTE)
                            .call();
                    
                    for (Ref ref : oldRemoteBranches) {
                        String refName = ref.getName();
                        if (refName.startsWith("refs/remotes/origin/")) {
                            git.branchDelete()
                                    .setBranchNames(refName)
                                    .setForce(true)
                                    .call();
                            log.info("删除旧远程分支引用: {}", refName);
                        }
                    }
                    log.info("清理旧的远程分支引用完成");
                } catch (Exception e) {
                    log.debug("清理远程分支引用失败: {}", e.getMessage());
                }
            } else {
                log.info("远程地址已是最新: {}", targetUrl);
            }
            
            // 统一执行 fetch，确保获取最新的远程分支信息
            log.info("开始获取远程分支信息...");
            git.fetch()
                    .setCredentialsProvider(credentials)
                    .setForceUpdate(true)  // 强制更新以确保获取最新分支信息
                    .call();
            log.info("远程分支信息获取完成");
            
        } catch (Exception e) {
            log.error("配置远程地址失败", e);
            throw new RuntimeException("配置远程地址失败: " + e.getMessage());
        }
    }

    @Override
    public List<BranchInfo> listBranches() {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            // 1. 确保远程地址配置正确并获取最新分支信息（内部已统一执行 fetch）
            ensureRemoteUrl(git);
            
            List<BranchInfo> branches = new ArrayList<>();

            // 只获取远程分支
            List<Ref> remoteBranches = git.branchList()
                    .setListMode(org.eclipse.jgit.api.ListBranchCommand.ListMode.REMOTE)
                    .call();
            
            log.info("找到 {} 个远程分支", remoteBranches.size());
            for (Ref ref : remoteBranches) {
                log.info("远程分支: {}", ref.getName());
            }
                    
            for (Ref ref : remoteBranches) {
                String branchName = ref.getName().replace("refs/remotes/origin/", "");
                // 过滤掉 HEAD
                if ("HEAD".equals(branchName)) {
                    continue;
                }

                BranchInfo info = new BranchInfo();
                info.setName(branchName);
                info.setType("remote");
                info.setIsCurrent(false);

                try (RevWalk walk = new RevWalk(git.getRepository())) {
                    ObjectId objectId = ref.getObjectId();
                    RevCommit commit = walk.parseCommit(objectId);
                    info.setLatestCommit(commit.getName());
                    info.setCommitMessage(commit.getShortMessage());
                } catch (Exception e) {
                    log.warn("获取远程分支信息失败: {}", e.getMessage());
                }

                branches.add(info);
            }

            return branches;
        } catch (GitAPIException | IOException e) {
            log.error("获取远程分支列表失败", e);
            throw new RuntimeException("获取远程分支列表失败: " + e.getMessage());
        }
    }

    @Override
    public BranchInfo createBranchWithRepo(CreateBranchWithRepoRequest request) {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            UsernamePasswordCredentialsProvider credentials =
                    new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());

            // 1. 确保远程地址配置正确并获取最新分支信息（内部已统一执行 fetch）
            ensureRemoteUrl(git);
            log.info("使用远程地址: {}", gitConfig.getRemoteUrl());
            
            // 2. 检查并清理本地分支（如果存在）
            String branchName = request.getBranchName();
            try {
                Ref existingBranch = git.getRepository().findRef("refs/heads/" + branchName);
                if (existingBranch != null) {
                    log.info("本地分支 {} 已存在，先删除: {}", branchName, existingBranch.getObjectId().getName());
                    git.branchDelete()
                            .setBranchNames(branchName)
                            .setForce(true)
                            .call();
                    log.info("已删除本地分支: {}", branchName);
                }
            } catch (Exception e) {
                log.debug("检查本地分支时出现异常（可能不存在）: {}", e.getMessage());
            }
            
            // 3. 基于远程分支创建新分支 (相当于 git branch feature/rel_1.0.1 origin/master)
            String baseBranch = request.getBaseBranch() != null ? request.getBaseBranch() : "master";
            Ref newBranchRef = git.branchCreate()
                    .setName(branchName)
                    .setStartPoint("origin/" + baseBranch)
                    .call();

            BranchInfo info = new BranchInfo();
            info.setName(branchName);
            info.setType("local");
            info.setIsCurrent(false);

            // 获取新分支指向的 commit 信息
            try (RevWalk walk = new RevWalk(git.getRepository())) {
                ObjectId objectId = newBranchRef.getObjectId();
                RevCommit commit = walk.parseCommit(objectId);
                info.setLatestCommit(commit.getName());
                info.setCommitMessage(commit.getShortMessage());
                log.info("新分支 {} 指向 commit: {} ({})", branchName, commit.getName(), commit.getShortMessage());
            } catch (Exception e) {
                log.warn("获取新分支 commit 信息失败: {}", e.getMessage());
            }

            // 4. 推送到远程 (相当于 git push -u origin feature/rel_1.0.1)
            if (request.getPushToRemote() != null && request.getPushToRemote()) {
                git.push()
                        .setRemote("origin")
                        .setCredentialsProvider(credentials)
                        .add(branchName)
                        .call();
                log.info("分支已推送到远程: {}", branchName);
            }

            log.info("创建分支成功: {}", branchName);
            return info;

        } catch (GitAPIException | IOException e) {
            log.error("创建分支失败", e);
            throw new RuntimeException("创建分支失败: " + e.getMessage());
        }
    }


    @Override
    public void checkoutBranch(String branchName) {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            git.checkout().setName(branchName).call();
            log.info("切换分支成功: {}", branchName);
        } catch (GitAPIException | IOException e) {
            log.error("切换分支失败", e);
            throw new RuntimeException("切换分支失败: " + e.getMessage());
        }
    }

    @Override
    public MergeResult mergeBranch(MergeBranchRequest request) {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            UsernamePasswordCredentialsProvider credentials = 
                new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
            
            // 1. 确保远程地址配置正确
            ensureRemoteUrl(git);
            
            // 2. 执行合并（直接合并到目标分支）
            // 获取源分支和目标分支的引用
            Ref sourceRef = findBranchRef(git, request.getSourceBranch());
            Ref targetRef = findBranchRef(git, request.getTargetBranch());
            
            if (sourceRef == null) {
                throw new RuntimeException("源分支不存在: " + request.getSourceBranch());
            }
            if (targetRef == null) {
                throw new RuntimeException("目标分支不存在: " + request.getTargetBranch());
            }
            
            // 确保目标分支存在，如果不存在则从远程分支创建
            ensureLocalBranchExists(git, request.getTargetBranch());
            
            // 切换到目标分支进行合并
            git.checkout().setName(request.getTargetBranch()).call();
            log.info("已切换到目标分支: {}", request.getTargetBranch());
            
            // 强制更新源分支到最新状态
            log.info("更新源分支 {} 到最新状态", request.getSourceBranch());
            try {
                git.fetch()
                        .setCredentialsProvider(credentials)
                        .setForceUpdate(true)
                        .call();
                log.info("源分支更新完成");
            } catch (Exception e) {
                log.warn("更新源分支失败: {}", e.getMessage());
            }
            
            // 重新获取源分支引用（确保是最新的）
            Ref updatedSourceRef = findBranchRef(git, request.getSourceBranch());
            if (updatedSourceRef == null) {
                throw new RuntimeException("更新后源分支不存在: " + request.getSourceBranch());
            }
            
            log.info("开始合并: {} -> {}", request.getSourceBranch(), request.getTargetBranch());
            log.info("源分支提交ID: {}", updatedSourceRef.getObjectId().getName());
            
            // 执行合并
            org.eclipse.jgit.api.MergeResult jgitResult = git.merge()
                    .include(updatedSourceRef.getObjectId())  // 使用最新的ObjectId
                    .setFastForward(MergeCommand.FastForwardMode.NO_FF)
                    .call();

            MergeResult mergeResult = new MergeResult();

            // 4. 检查合并状态
            log.info("合并状态: {}", jgitResult.getMergeStatus());
            
            if (jgitResult.getMergeStatus().isSuccessful()) {
                mergeResult.setSuccess(true);
                mergeResult.setStatus("SUCCESS");
                mergeResult.setMessage("合并成功");

                if (jgitResult.getNewHead() != null) {
                    mergeResult.setMergeCommit(jgitResult.getNewHead().getName());
                    log.info("合并提交ID: {}", jgitResult.getNewHead().getName());
                } else {
                    log.warn("合并成功但没有新的提交ID");
                }

                // 5. 推送到远程
                if (request.getPushToRemote() != null && request.getPushToRemote()) {
                    try {
                        git.push()
                                .setRemote("origin")
                                .setCredentialsProvider(credentials)
                                .add(request.getTargetBranch())  // 明确指定要推送的分支
                                .call();
                        log.info("合并已推送到远程: {}", request.getTargetBranch());
                    } catch (Exception e) {
                        log.error("推送失败: {}", e.getMessage());
                        // 推送失败不影响合并结果，但需要记录
                    }
                }
            } else if (jgitResult.getMergeStatus() == org.eclipse.jgit.api.MergeResult.MergeStatus.CONFLICTING) {
                mergeResult.setSuccess(false);
                mergeResult.setStatus("CONFLICT");
                mergeResult.setMessage("存在合并冲突");
                mergeResult.setConflictFiles(new ArrayList<>(jgitResult.getConflicts().keySet()));

                log.warn("合并冲突: {}", jgitResult.getConflicts());
            } else {
                mergeResult.setSuccess(false);
                mergeResult.setStatus("FAILED");
                mergeResult.setMessage("合并失败: " + jgitResult.getMergeStatus());
            }

            return mergeResult;
        } catch (GitAPIException | IOException e) {
            log.error("合并分支失败", e);
            throw new RuntimeException("合并分支失败: " + e.getMessage());
        }
    }

    @Override
    public List<String> detectConflicts(String sourceBranch, String targetBranch) {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            git.checkout().setName(targetBranch).call();

            org.eclipse.jgit.api.MergeResult result = git.merge()
                    .include(git.getRepository().resolve(sourceBranch))
                    .setCommit(false)
                    .call();

            if (result.getMergeStatus() == org.eclipse.jgit.api.MergeResult.MergeStatus.CONFLICTING) {
                return new ArrayList<>(result.getConflicts().keySet());
            }

            return new ArrayList<>();
        } catch (GitAPIException | IOException e) {
            log.error("检测冲突失败", e);
            throw new RuntimeException("检测冲突失败: " + e.getMessage());
        }
    }

    @Override
    public void rollback(RollbackRequest request) {
        try (Git git = Git.open(new File(getRepositoryPath()))) {
            ResetCommand.ResetType resetType;
            switch (request.getResetType().toUpperCase()) {
                case "SOFT":
                    resetType = ResetCommand.ResetType.SOFT;
                    break;
                case "HARD":
                    resetType = ResetCommand.ResetType.HARD;
                    break;
                default:
                    resetType = ResetCommand.ResetType.MIXED;
            }

            git.reset()
                    .setMode(resetType)
                    .setRef(request.getTargetCommit())
                    .call();

            log.info("回滚成功: {} -> {}", git.getRepository().getBranch(), request.getTargetCommit());
        } catch (GitAPIException | IOException e) {
            log.error("回滚失败", e);
            throw new RuntimeException("回滚失败: " + e.getMessage());
        }
    }

    @Override
    public String pullBranchAndCreateTestFile(String branchName) {
        String targetDir = "D:\\my_project\\git_test_file";
        File targetPath = new File(targetDir);
        
        try {
            Git git;
            
            // 判断目录是否存在，如果不存在则克隆，存在则打开
            if (!targetPath.exists() || !new File(targetPath, ".git").exists()) {
                log.info("目录不存在或不是Git仓库，开始克隆: {}", targetDir);
                // 克隆远程仓库
                git = Git.cloneRepository()
                        .setURI(gitConfig.getRemoteUrl())
                        .setDirectory(targetPath)
                        .setBranch(branchName)
                        .setCredentialsProvider(new UsernamePasswordCredentialsProvider(
                                gitConfig.getUsername(), gitConfig.getPassword()))
                        .call();
                log.info("克隆成功: {}", targetDir);
            } else {
                log.info("打开现有仓库: {}", targetDir);
                git = Git.open(targetPath);
                
                // 确保远程地址正确（即使远程地址更新并执行了全量 fetch，这里仍需要特定分支的 fetch）
                ensureRemoteUrl(git);
                
                // 拉取指定分支（使用特定分支的 fetch，确保获取指定分支的最新信息）
                UsernamePasswordCredentialsProvider credentials = 
                    new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
                
                git.fetch()
                        .setCredentialsProvider(credentials)
                        .setRefSpecs(new org.eclipse.jgit.transport.RefSpec("+refs/heads/" + branchName + ":refs/heads/" + branchName))
                        .call();
                
                // 检查本地分支是否存在
                Ref localBranchRef = git.getRepository().findRef("refs/heads/" + branchName);
                
                if (localBranchRef != null) {
                    // 本地分支已存在，直接切换
                    log.info("本地分支 {} 已存在，直接切换", branchName);
                    git.checkout()
                            .setName(branchName)
                            .call();
                } else {
                    // 本地分支不存在，创建并切换
                    log.info("创建并切换到本地分支: {}", branchName);
                    git.checkout()
                            .setName(branchName)
                            .setCreateBranch(true)
                            .setUpstreamMode(org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode.TRACK)
                            .setStartPoint("origin/" + branchName)
                            .call();
                }
                
                // 拉取最新代码
                git.pull()
                        .setCredentialsProvider(credentials)
                        .setRemoteBranchName(branchName)
                        .call();
                log.info("拉取成功: {}", branchName);
            }
            
            // 生成并打印 Maven 依赖树（针对拉取下来的项目目录）
            try {
                String dependencyTreeJson = MavenDependencyCounter.buildDependencyTreeJson(targetPath.toPath());
                log.info("Maven 依赖树: {}", dependencyTreeJson);
            } catch (Exception e) {
                log.warn("生成 Maven 依赖树失败: {}", e.getMessage());
            }
            
            // 生成 test.properties 文件
            File testPropertiesFile = new File(targetPath, "test1.properties");
            String content = "# Test Properties File\n" +
                           "# Generated by Git Management System\n" +
                           "app.name=GitTestApp\n" +
                           "app.version=1.0.0\n" +
                           "test.mode=true\n" +
                           "test.timestamp=" + System.currentTimeMillis() + "\n" +
                           "test.description=This is a test properties file generated automatically\n";
            
            try (java.io.FileWriter writer = new java.io.FileWriter(testPropertiesFile)) {
                writer.write(content);
            }
            log.info("生成test.properties文件成功: {}", testPropertiesFile.getAbsolutePath());
            
            // 添加文件到暂存区
            git.add()
                    .addFilepattern("test.properties")
                    .call();
            log.info("添加test.properties到暂存区成功");
            
            // 提交
            git.commit()
                    .setMessage("Add test.properties file automatically")
                    .setAuthor("Git Management System", "system@example.com")
                    .call();
            log.info("提交成功");
            
            // 推送到远程
            UsernamePasswordCredentialsProvider credentials = 
                new UsernamePasswordCredentialsProvider(gitConfig.getUsername(), gitConfig.getPassword());
            
            git.push()
                    .setRemote("origin")
                    .setCredentialsProvider(credentials)
                    .add(branchName)
                    .call();
            log.info("推送到远程成功: {}", branchName);
            
            git.close();
            
            return String.format("操作成功！分支 %s 已拉取到 %s，test.properties 文件已生成并推送到远程", 
                    branchName, targetDir);
                    
        } catch (GitAPIException | IOException e) {
            log.error("拉取分支并创建测试文件失败", e);
            throw new RuntimeException("操作失败: " + e.getMessage());
        }
    }

}
